/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License,Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.harmonyos.studyharmonyos.slice;

import com.harmonyos.studyharmonyos.ResourceTable;
import com.harmonyos.studyharmonyos.service.VideoMigrateService;
import com.harmonyos.studyharmonyos.component.RemoteController;
import com.harmonyos.studyharmonyos.component.SlidePopupWindow;
import com.harmonyos.studyharmonyos.component.Toast;
import com.harmonyos.studyharmonyos.helper.provider.CommonProvider;
import com.harmonyos.studyharmonyos.helper.provider.ViewProvider;
import com.harmonyos.studyharmonyos.idl.ImplVideoMigration;
import com.harmonyos.studyharmonyos.idl.VideoMigrationStub;
import com.harmonyos.studyharmonyos.player.HmPlayer;
import com.harmonyos.studyharmonyos.player.api.ImplPlayer;
import com.harmonyos.studyharmonyos.player.constant.Constants;
import com.harmonyos.studyharmonyos.player.constant.PlayerStatu;
import com.harmonyos.studyharmonyos.player.view.PlayerController;
import com.harmonyos.studyharmonyos.player.view.PlayerLoading;
import com.harmonyos.studyharmonyos.player.view.PlayerView;
import com.harmonyos.studyharmonyos.util.AbilitySliceRouteUtil;
import com.harmonyos.studyharmonyos.util.LogUtils;
import com.harmonyos.studyharmonyos.util.ScreenUtils;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.ability.IAbilityConnection;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.Operation;
import ohos.agp.components.*;
import ohos.app.dispatcher.task.TaskPriority;
import ohos.bundle.AbilityInfo;
import ohos.bundle.ElementName;
import ohos.distributedschedule.interwork.DeviceInfo;
import ohos.distributedschedule.interwork.DeviceManager;
import ohos.rpc.IRemoteObject;
import ohos.rpc.RemoteException;

import java.util.ArrayList;
import java.util.List;

/**
 * 视频播放页面
 *
 * @since 2020-12-04
 */
public class VedioPlayerAbilitySlice extends AbilitySlice {
    private static final String TAG = VedioPlayerAbilitySlice.class.getSimpleName();
    private static final int TOAST_DURATION = 3000;
    private static ImplPlayer player;
    private RemoteController remoteController;//视频控制自定义控件
    private ImplVideoMigration implVideoMigration;//服务端代理
    private DependentLayout parentLayout;//页面根布局
    private ListContainer deviceListContainer;//显示设备列表
    private Image tv;
    private SlidePopupWindow transWindow;//设备弹窗
    private List<DeviceInfo> devices = new ArrayList<>(0);//设备列表数据
    private int startMillisecond;
    private int video_index;

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_video_play_layout);
        AbilitySliceRouteUtil.getInstance().addRoute(this);
        startMillisecond = intent.getIntParam(Constants.INTENT_STARTTIME_PARAM, 0);//获取上个页面传递的参数
        video_index = intent.getIntParam(Constants.VIDEO_URL, 0);//视频资源下标
        player = new HmPlayer.Builder(this).setFilePath(Constants.VIDEO_PATH[video_index]).setStartMillisecond(startMillisecond).create();//创建视频播放控制器
        player.getLifecycle().onStart();//开始播放
        initComponent();
        initListener();
    }

    /**
     * getImplPlayer
     *
     * @return ImplPlayer
     */
    public static ImplPlayer getImplPlayer() {
        return player;
    }

    /**
     * 初始化视频播放、提示、视频控制等控件，并绑定视频
     */
    private void initComponent() {
        if (findComponentById(ResourceTable.Id_parent) instanceof DependentLayout) {
            parentLayout = (DependentLayout) findComponentById(ResourceTable.Id_parent);//视频播放布局
            transWindow = new SlidePopupWindow.Builder(this).create(ResourceTable.Layout_trans_slide);//选择设备弹窗布局
            if (transWindow.findComponentById(ResourceTable.Id_device_list_container) instanceof ListContainer) {//控件类型判断
                deviceListContainer = (ListContainer) transWindow.findComponentById(ResourceTable.Id_device_list_container);
            }
            remoteController = new RemoteController(this);//视频控制自定义控件
            parentLayout.addComponent(transWindow);
            parentLayout.addComponent(remoteController);
        }
        if (findComponentById(ResourceTable.Id_player_view) instanceof PlayerView) {
            PlayerView playerView = (PlayerView) findComponentById(ResourceTable.Id_player_view);//视频播放显示控件
            playerView.bind(player);
        }
        if (findComponentById(ResourceTable.Id_loading_view) instanceof PlayerLoading) {
            PlayerLoading playerLoading = (PlayerLoading) findComponentById(ResourceTable.Id_loading_view);//视频加载中提示框控件
            playerLoading.bind(player);
        }
        if (findComponentById(ResourceTable.Id_controller_view) instanceof PlayerController) {
            PlayerController simplePlayerController =
                    (PlayerController) findComponentById(ResourceTable.Id_controller_view);//视频播放控制控件
            if (simplePlayerController.findComponentById(ResourceTable.Id_tv) instanceof Image) {
                tv = (Image) simplePlayerController.findComponentById(ResourceTable.Id_tv);
            }
            simplePlayerController.bind(player);
        }
    }

    /**
     * 初始化控件单击监听
     */
    private void initListener() {
        tv.setClickedListener(component -> {
            initDevices();
            showDeviceList();
        });
        /* 监听选择设备弹出框显示与隐藏事件 显示时暂停视频播放  隐藏时如果视频是暂定状态则继续播放 */
        transWindow.setPopupWindowListener(new SlidePopupWindow.PopupWindowListener() {
            @Override
            public void windowShow() {
                player.pause();//播放暂停
            }

            @Override
            public void windowDismiss() {
                if (player.getPlayerStatu() == PlayerStatu.PAUSE) {
                    player.resume(); //继续播放
                }
            }
        });
        setRemoteControllerCallback();//绑定视频控制自定义控件事件监听
    }

    private void setRemoteControllerCallback() {
        remoteController.setRemoteControllerCallback(new MyRemoteControllerListener());
    }

    /**
     * 控制器接口实现
     *
     * @since 2020-12-04
     */
    private class MyRemoteControllerListener implements RemoteController.RemoteControllerListener {
        @Override
        public void controllerShow() {
        }

        @Override
        public void controllerDismiss() {//视频控制自定义控件隐藏时，退出多端协同
            int progress = 0;
            try {
                if (implVideoMigration != null) {
                    progress = implVideoMigration.flyOut();//关闭对端PA，并返回当前播放进度
                }
            } catch (RemoteException e) {
                LogUtils.error(TAG, "RemoteException occurs");
            }
            player.reload(Constants.VIDEO_PATH[video_index], progress);//本端按进度继续播放视频
        }

        @Override
        public void sendControl(int code, int extra) {//发送控制指令
            try {
                if (implVideoMigration != null) {
                    implVideoMigration.playControl(code, extra);
                }
            } catch (RemoteException e) {
                LogUtils.error(TAG, "RemoteException occurs ");
            }
        }
    }

    /**
     * 初始化获取在线设备
     */
    private void initDevices() {
        if (devices.size() > 0) {
            devices.clear();
        }
        List<DeviceInfo> deviceInfos = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);
        devices.addAll(deviceInfos);
    }

    /**
     * 显示设备列表
     */
    private void showDeviceList() {
        CommonProvider<DeviceInfo> commonProvider = new CommonProvider<DeviceInfo>(//创建设备显示列表适配器
                devices,
                getContext(),
                ResourceTable.Layout_device_list_item) {
            @Override
            protected void convert(ViewProvider holder, DeviceInfo item, int position) {
                holder.setText(ResourceTable.Id_device_text, item.getDeviceName());
                Button clickButton = holder.getView(ResourceTable.Id_device_text);
                clickButton.setText(item.getDeviceName());
                clickButton.setClickedListener(component -> {//选择设备后，隐藏弹框，并开始迁移
                    transWindow.hide();
                    startAbilityFa(item.getDeviceId());
                });
            }
        };
        deviceListContainer.setItemProvider(commonProvider);//绑定适配器
        commonProvider.notifyDataChanged();//刷新列表
        transWindow.show();//显示弹框
    }

    /**
     * 开始迁移
     *
     * @param devicesId 设备ID
     */
    private void startAbilityFa(String devicesId) {
        Intent intent = new Intent();
        Operation operation =
                new Intent.OperationBuilder()
                        .withDeviceId(devicesId)
                        .withBundleName(getBundleName())
                        .withAbilityName(VideoMigrateService.class.getName())//注意1：这里去连接对端的此服务
                        .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
                        .build();
        intent.setOperation(operation);
        //用于管理连接关系
        boolean connectFlag = connectAbility(intent, new IAbilityConnection() {
            @Override
            public void onAbilityConnectDone(ElementName elementName, IRemoteObject remoteObject, int resultCode) {
                // 跨设备PA连接完成后，会返回一个序列化的IRemoteObject对象
                // 通过该对象得到控制远端服务的代理
                implVideoMigration = VideoMigrationStub.asInterface(remoteObject);
                try {
                    //连接完成后、传递播放进度、视频源信息
                    implVideoMigration.flyIn(startMillisecond, video_index);//注意2：这里调用对端服务的flyIn方法，而该方法会去启动视频播放页面，并传递相应参数
                } catch (RemoteException e) {
                    LogUtils.error(TAG, "connect successful,but have remote exception");
                }
            }
            @Override
            public void onAbilityDisconnectDone(ElementName elementName, int resultCode) {
                // 当已连接的远端PA异常关闭时，会触发该回调
                // 支持按照返回的错误信息resultCode进行PA生命周期管理
                disconnectAbility(this);
            }
        });
        if (connectFlag) {
            Toast.toast(this, "transmit successful！", TOAST_DURATION);
            remoteController.show();//显示视频自定义控件
            startMillisecond = player.getCurrentPosition();//获取当前播放进度
            player.release();
        } else {
            Toast.toast(this, "transmit failed!Please try again later.", TOAST_DURATION);
        }
    }

    @Override
    public void onActive() {
        super.onActive();
        getGlobalTaskDispatcher(TaskPriority.DEFAULT).delayDispatch(() -> player.play(), Constants.NUMBER_1000);
    }

    @Override
    protected void onInactive() {
        LogUtils.info(TAG, "onInactive is called");
        super.onInactive();
    }

    @Override
    public void onForeground(Intent intent) {
        if (remoteController == null || !remoteController.isShown()) {
            player.getLifecycle().onForeground();
        }
        super.onForeground(intent);
    }

    @Override
    protected void onBackground() {
        LogUtils.info(TAG, "onBackground is called");
        if (remoteController == null || !remoteController.isShown()) {
            player.getLifecycle().onBackground();
        }
        super.onBackground();
    }

    @Override
    protected void onBackPressed() {
        if (remoteController != null && remoteController.isShown()) {
            remoteController.hide();
        } else {
            super.onBackPressed();
        }
    }

    @Override
    protected void onStop() {
        LogUtils.info(TAG, "onStop is called");
        AbilitySliceRouteUtil.getInstance().removeRoute(this);
        player.getLifecycle().onStop();
        super.onStop();
    }

    @Override
    protected void onOrientationChanged(AbilityInfo.DisplayOrientation displayOrientation) {
        super.onOrientationChanged(displayOrientation);
        int screenWidth = ScreenUtils.getScreenWidth(this);
        parentLayout.setWidth(screenWidth);
        tv.setVisibility(displayOrientation == AbilityInfo.DisplayOrientation.LANDSCAPE
                ? Component.HIDE
                : Component.VISIBLE);
        player.openGesture(displayOrientation == AbilityInfo.DisplayOrientation.LANDSCAPE);
    }
}
